﻿using System;
using System.Security;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Management;
using System.Linq;
using System.Collections;

namespace PowerTriggersWcf
{
    /// <summary>
    /// Class that allows running applications with full admin rights. In
    /// addition the application launched will bypass the Vista UAC prompt.
    /// </summary>
    internal class ProcessLauncher
    {
        private static Hashtable _fileVersionInfoCache = Hashtable.Synchronized(new Hashtable());

        #region Structures

        [StructLayout(LayoutKind.Sequential)]
        private struct SECURITY_ATTRIBUTES
        {
            public int Length;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct STARTUPINFO
        {
            public int cb;
            public String lpReserved;
            public String lpDesktop;
            public String lpTitle;
            public uint dwX;
            public uint dwY;
            public uint dwXSize;
            public uint dwYSize;
            public uint dwXCountChars;
            public uint dwYCountChars;
            public uint dwFillAttribute;
            public uint dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct WTS_SESSION_INFO
        {
            public Int32 SessionID; //DWORD integer
            public string pWinStationName; // integer LPTSTR - Pointer to a null-terminated string containing the name of the WinStation for this session
            public WTS_CONNECTSTATE_CLASS State;
        }

        #endregion

        #region Enumerations

        enum TOKEN_TYPE : int
        {
            TokenPrimary = 1,
            TokenImpersonation = 2
        }

        enum SECURITY_IMPERSONATION_LEVEL : int
        {
            SecurityAnonymous = 0,
            SecurityIdentification = 1,
            SecurityImpersonation = 2,
            SecurityDelegation = 3,
        }

        public enum WTS_CONNECTSTATE_CLASS
        {
            WTSActive,
            WTSConnected,
            WTSConnectQuery,
            WTSShadow,
            WTSDisconnected,
            WTSIdle,
            WTSListen,
            WTSReset,
            WTSDown,
            WTSInit
        }

        private enum WTS_INFO_CLASS
        {
            WTSInitialProgram = 0,
            WTSApplicationName = 1,
            WTSWorkingDirectory = 2,
            WTSOEMId = 3,
            WTSSessionId = 4,
            WTSUserName = 5,
            WTSWinStationName = 6,
            WTSDomainName = 7,
            WTSConnectState = 8,
            WTSClientBuildNumber = 9,
            WTSClientName = 10,
            WTSClientDirectory = 11,
            WTSClientProductId = 12,
            WTSClientHardwareId = 13,
            WTSClientAddress = 14,
            WTSClientDisplay = 15,
            WTSClientProtocolType = 16,
            WTSIdleTime = 17,
            WTSLogonTime = 18,
            WTSIncomingBytes = 19,
            WTSOutgoingBytes = 20,
            WTSIncomingFrames = 21,
            WTSOutgoingFrames = 22
        }
        #endregion

        #region Constants

        private const int TOKEN_DUPLICATE = 0x0002;
        private const uint MAXIMUM_ALLOWED = 0x2000000;
        private const int CREATE_NEW_CONSOLE = 0x00000010;

        private const int IDLE_PRIORITY_CLASS = 0x40;
        private const int NORMAL_PRIORITY_CLASS = 0x20;
        private const int HIGH_PRIORITY_CLASS = 0x80;
        private const int REALTIME_PRIORITY_CLASS = 0x100;

        private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;

        #endregion

        #region Win32 API Imports

        #region kernel32

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool CloseHandle(IntPtr hSnapshot);

        [DllImport("kernel32.dll")]
        static extern uint WTSGetActiveConsoleSessionId();

        [DllImport("kernel32.dll")]
        static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);

        [DllImport("kernel32.dll")]
        static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

        [DllImport("kernel32.dll", SetLastError = true)]
        [PreserveSig]
        private static extern uint GetModuleFileName(
                                [In]IntPtr hModule,
                                [Out]StringBuilder lpFilename,
                                [In][MarshalAs(UnmanagedType.U4)]int nSize
                    );
        //[DllImport("kernel32.dll", SetLastError = true)]
        //[PreserveSig]
        //private static extern uint GetModuleFileNameEx(
        //                        [In]IntPtr hProcess,
        //                        [In]IntPtr hModule,
        //                        [Out]StringBuilder lpFilename,
        //                        [In][MarshalAs(UnmanagedType.U4)]int nSize
        //            );
        [DllImport("kernel32.dll", ExactSpelling = true, EntryPoint = "QueryFullProcessImageNameW", CharSet = CharSet.Unicode)]
        static extern bool QueryFullProcessImageName(IntPtr hProcess, uint dwFlags, StringBuilder lpExeName, ref uint lpdwSize);

        //static extern uint GetLogicalDriveStrings(uint nBufferLength, [Out] char[] lpBuffer);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern int QueryDosDevice(
            [In] [Optional] string DeviceName,
            [In] StringBuilder lpExeName,
            [In] int MaxLength
            );

        #endregion

        #region advapi
        [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        private extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
            ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
            String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("advapi32.dll", SetLastError = true)]
        private extern static bool ImpersonateLoggedOnUser(IntPtr hToken);

        [DllImport("advapi32.dll", SetLastError = true)]
        private extern static bool RevertToSelf();

        [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
        private extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
            ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
            int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);

        [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
        static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);

        #endregion

        #region userenv
        [DllImport("userenv.dll", SetLastError = true)]
        private extern static bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);

        [DllImport("userenv.dll", SetLastError = true)]
        private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
        #endregion

        #region wtsapi
        [DllImport("wtsapi32.dll", SetLastError = true)]
        static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName);

        [DllImport("wtsapi32.dll")]
        static extern void WTSCloseServer(IntPtr hServer);

        [DllImport("wtsapi32.dll", SetLastError = true)]
        static extern Int32 WTSEnumerateSessions(
            IntPtr hServer,
            [MarshalAs(UnmanagedType.U4)] Int32 Reserved,
            [MarshalAs(UnmanagedType.U4)] Int32 Version,
            ref IntPtr ppSessionInfo,
            [MarshalAs(UnmanagedType.U4)] ref Int32 pCount);

        [DllImport("wtsapi32.dll")]
        static extern void WTSFreeMemory(IntPtr pMemory);

        /// <summary>
        /// The WTSQuerySessionInformation function retrieves session information for the specified 
        /// session on the specified terminal server. 
        /// It can be used to query session information on local and remote terminal servers.
        /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/termserv/termserv/wtsquerysessioninformation.asp
        /// </summary>
        /// <param name="hServer">Handle to a terminal server. Specify a handle opened by the WTSOpenServer function, 
        /// or specify <see cref="WTS_CURRENT_SERVER_HANDLE"/> to indicate the terminal server on which your application is running.</param>
        /// <param name="sessionId">A Terminal Services session identifier. To indicate the session in which the calling application is running 
        /// (or the current session) specify <see cref="WTS_CURRENT_SESSION"/>. Only specify <see cref="WTS_CURRENT_SESSION"/> when obtaining session information on the 
        /// local server. If it is specified when querying session information on a remote server, the returned session 
        /// information will be inconsistent. Do not use the returned data in this situation.</param>
        /// <param name="wtsInfoClass">Specifies the type of information to retrieve. This parameter can be one of the values from the <see cref="WTSInfoClass"/> enumeration type. </param>
        /// <param name="ppBuffer">Pointer to a variable that receives a pointer to the requested information. The format and contents of the data depend on the information class specified in the <see cref="WTSInfoClass"/> parameter. 
        /// To free the returned buffer, call the <see cref="WTSFreeMemory"/> function. </param>
        /// <param name="pBytesReturned">Pointer to a variable that receives the size, in bytes, of the data returned in ppBuffer.</param>
        /// <returns>If the function succeeds, the return value is a nonzero value.
        /// If the function fails, the return value is zero. To get extended error information, call GetLastError.
        /// </returns>
        [DllImport("Wtsapi32.dll")]
        private static extern bool WTSQuerySessionInformation(System.IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out System.IntPtr ppBuffer, out uint pBytesReturned);
        #endregion

        #region psapi
        [DllImport("psapi.dll")]
        static extern uint GetProcessImageFileName(IntPtr hProcess, [Out] StringBuilder lpImageFileName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);
        #endregion

        #endregion

        ///// <summary>
        ///// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt, for all logged on users
        ///// </summary>
        ///// <param name="applicationName">The name of the application to launch</param>
        ///// <param name="procInfo">Process information regarding the launched application that gets returned to the caller</param>
        ///// <returns></returns>
        //public static bool StartProcessAndBypassUAC(String applicationName, out IList<PROCESS_INFORMATION> procInfos)
        //{
        //    List<uint> winlogonPids = new List<uint>();
        //    //IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
        //    Dictionary<uint, PROCESS_INFORMATION> tempProcInfo = new Dictionary<uint, PROCESS_INFORMATION>();

        //    // obtain the currently active session id; every logged on user in the system has a unique session id
        //    uint dwSessionId = WTSGetActiveConsoleSessionId();

        //    // obtain the process id of the winlogon process that is running within the currently active session
        //    Process[] processes = Process.GetProcessesByName("winlogon");
        //    foreach (Process p in processes)
        //    {
        //        if ((uint)p.SessionId == dwSessionId)
        //        {
        //            winlogonPids.Add((uint)p.Id);
        //            tempProcInfo.Add((uint)p.Id, new PROCESS_INFORMATION());
        //        }
        //    }

        //    bool result = winlogonPids.Count > 0;
        //    procInfos = new List<PROCESS_INFORMATION>(winlogonPids.Count);
        //    foreach (uint winlogonPid in winlogonPids)
        //    {
        //        PROCESS_INFORMATION procInfo = tempProcInfo[winlogonPid];
        //        if (StartProcessAndBypassUAC(applicationName, winlogonPid, out procInfo))
        //        {
        //            tempProcInfo[winlogonPid] = procInfo;
        //            procInfos.Add(procInfo);
        //        }
        //        else
        //        {
        //            result = false;
        //        }
        //    }

        //    return result; // return the result
        //}

        ///// <summary>
        ///// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt, for currently active user (ie the user on the console)
        ///// </summary>
        ///// <param name="applicationName">The name of the application to launch</param>
        ///// <param name="procInfo">Process information regarding the launched application that gets returned to the caller</param>
        ///// <returns></returns>
        //public static bool StartProcessAndBypassUAC(String applicationName, out PROCESS_INFORMATION procInfo)
        //{
        //    uint winlogonPid = 0;
        //    //IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
        //    //procInfo = new PROCESS_INFORMATION();

        //    // obtain the currently active session id; every logged on user in the system has a unique session id
        //    uint dwSessionId = WTSGetActiveConsoleSessionId();

        //    // obtain the process id of the winlogon process that is running within the currently active session
        //    Process[] processes = Process.GetProcessesByName("winlogon");
        //    foreach (Process p in processes)
        //    {
        //        if ((uint)p.SessionId == dwSessionId)
        //        {
        //            winlogonPid = (uint)p.Id;
        //        }
        //        p.Dispose();
        //    }

        //    if (winlogonPid == 0)
        //    {
        //        procInfo = new PROCESS_INFORMATION();
        //        return false;
        //    }
        //    else
        //    {
        //        return StartProcessAndBypassUAC(applicationName, winlogonPid, out procInfo);
        //    }
        //}

        ///// <summary>
        ///// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt (for the specified username)
        ///// </summary>
        ///// <param name="applicationName">The name of the application to launch</param>
        ///// <param name="procInfo">Process information regarding the launched application that gets returned to the caller</param>
        ///// <returns></returns>
        //public static bool StartProcessAndBypassUAC(String applicationName, out PROCESS_INFORMATION procInfo, string username)
        //{
        //    username = username.ToLower();
        //    uint winlogonPid = 0;
        //    //IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
        //    //procInfo = new PROCESS_INFORMATION();

        //    //// obtain the currently active session id; every logged on user in the system has a unique session id
        //    //uint dwSessionId = WTSGetActiveConsoleSessionId();

        //    // obtain the process id of the winlogon process that is running within the currently active session
        //    Process[] processes = Process.GetProcessesByName("winlogon");
        //    foreach (Process p in processes)
        //    {
        //        //if ((uint)p.SessionId == dwSessionId)
        //        if (p.StartInfo != null && p.StartInfo.UserName != null && p.StartInfo.UserName.ToLower() == username)
        //        {
        //            winlogonPid = (uint)p.Id;
        //        }
        //        p.Dispose();
        //    }

        //    if (winlogonPid == 0)
        //    {
        //        procInfo = new PROCESS_INFORMATION();
        //        return false;
        //    }
        //    else
        //    {
        //        return StartProcessAndBypassUAC(applicationName, winlogonPid, out procInfo);
        //    }
        //}

        /// <summary>
        /// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt
        /// </summary>
        /// <param name="applicationName">The name of the application to launch</param>
        /// <param name="procInfo">Process information regarding the launched application that gets returned to the caller</param>
        /// <returns></returns>
        internal static void StartProcessAndBypassUAC(String applicationName, int dwSessionId, bool duplicateEnvironmentBlock, out PROCESS_INFORMATION procInfo)
        {
            //IntPtr hUserTokenDup = IntPtr.Zero;
            //IntPtr hPToken = IntPtr.Zero;
            //IntPtr hProcess = IntPtr.Zero;
            procInfo = new PROCESS_INFORMATION();

            //// obtain the currently active session id; every logged on user in the system has a unique session id
            //uint dwSessionId = WTSGetActiveConsoleSessionId();

            // obtain the process id of the winlogon process that is running within the currently active session
            Process[] processes = Process.GetProcessesByName("winlogon");
            uint winlogonPid = 0;
            foreach (Process p in processes)
            {
                if ((uint)p.SessionId == dwSessionId)
                {
                    winlogonPid = (uint)p.Id;
                }
                p.Dispose();
            }
            if (winlogonPid == 0)
            {
                throw new Exception("Cannot find sessionId: " + dwSessionId.ToString());
            }

            // obtain a handle to the winlogon process
            IntPtr hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

            // obtain a handle to the access token of the winlogon process
            IntPtr hPToken = IntPtr.Zero;
            if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
            {
                CloseHandle(hProcess);
                throw new Win32Exception(Marshal.GetLastWin32Error());
                //return false;
            }

            // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
            // I would prefer to not have to use a security attribute variable and to just 
            // simply pass null and inherit (by default) the security attributes
            // of the existing token. However, in C# structures are value types and therefore
            // cannot be assigned the null value.
            SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
            sa.Length = Marshal.SizeOf(sa);

            IntPtr hUserTokenDup = IntPtr.Zero;
            // copy the access token of the winlogon process; the newly created token will be a primary token
            if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
            {
                CloseHandle(hPToken);
                CloseHandle(hProcess);
                throw new Win32Exception(Marshal.GetLastWin32Error());
                //return false;
            }

            IntPtr hEnvBlock = duplicateEnvironmentBlock ? GetEnvironmentBlock(hUserTokenDup) : IntPtr.Zero;

            CloseHandle(hPToken); // no longer needed

            bool impersonated = ImpersonateLoggedOnUser(hUserTokenDup);
            bool result;
            try
            {
                // By default CreateProcessAsUser creates a process on a non-interactive window station, meaning
                // the window station has a desktop that is invisible and the process is incapable of receiving
                // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user 
                // interaction with the new process.
                STARTUPINFO si = new STARTUPINFO();
                si.cb = (int)Marshal.SizeOf(si);
                si.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop

                // flags that specify the priority and creation method of the process
                int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

                // create a new process in the current user's logon session
                result = CreateProcessAsUser(hUserTokenDup,        // client's access token
                                                null,                   // file to execute
                                                applicationName,        // command line
                                                ref sa,                 // pointer to process SECURITY_ATTRIBUTES
                                                ref sa,                 // pointer to thread SECURITY_ATTRIBUTES
                                                false,                  // handles are not inheritable
                                                dwCreationFlags,        // creation flags
                                                hEnvBlock,            // pointer to new environment block 
                                                null,                   // name of current directory 
                                                ref si,                 // pointer to STARTUPINFO structure
                                                out procInfo            // receives information about new process
                                                );
            }
            finally
            {
                if (impersonated)
                {
                    RevertToSelf();
                }
            }

            if (hEnvBlock != IntPtr.Zero)
            {
                DestroyEnvironmentBlock(hEnvBlock);
            }

            // invalidate the handles
            CloseHandle(hUserTokenDup);
            //CloseHandle(hPToken);
            CloseHandle(hProcess);

            if (!result)
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            // return true;
        }

        ///// <summary>
        ///// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt
        ///// </summary>
        ///// <param name="applicationName">The name of the application to launch</param>
        ///// <param name="procInfo">Process information regarding the launched application that gets returned to the caller</param>
        ///// <returns></returns>
        //internal static void StartProcessAndBypassUAC(String applicationName, string username, string password, out PROCESS_INFORMATION procInfo)
        //{
        //    //IntPtr hUserTokenDup = IntPtr.Zero;
        //    //IntPtr hPToken = IntPtr.Zero;
        //    //IntPtr hProcess = IntPtr.Zero;
        //    procInfo = new PROCESS_INFORMATION();

        //    //// obtain the currently active session id; every logged on user in the system has a unique session id
        //    //uint dwSessionId = WTSGetActiveConsoleSessionId();

        //    // obtain the process id of the winlogon process that is running within the currently active session
        //    //Process[] processes = Process.GetProcessesByName("winlogon");
        //    //uint winlogonPid = 0;
        //    //foreach (Process p in processes)
        //    //{
        //    //    if ((uint)p.SessionId == dwSessionId)
        //    //    {
        //    //        winlogonPid = (uint)p.Id;
        //    //    }
        //    //    p.Dispose();
        //    //}
        //    //if (winlogonPid == 0)
        //    //{
        //    //    throw new Exception("Cannot find sessionId: " + dwSessionId.ToString());
        //    //}

        //    // obtain a handle to the winlogon process
        //    //IntPtr hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

        //    // obtain a handle to the access token of the winlogon process
        //    IntPtr hPToken = IntPtr.Zero;
        //    if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
        //    {
        //        CloseHandle(hProcess);
        //        throw new Win32Exception(Marshal.GetLastWin32Error());
        //        //return false;
        //    }

        //    // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
        //    // I would prefer to not have to use a security attribute variable and to just 
        //    // simply pass null and inherit (by default) the security attributes
        //    // of the existing token. However, in C# structures are value types and therefore
        //    // cannot be assigned the null value.
        //    SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
        //    sa.Length = Marshal.SizeOf(sa);

        //    IntPtr hUserTokenDup = IntPtr.Zero;
        //    // copy the access token of the winlogon process; the newly created token will be a primary token
        //    if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
        //    {
        //        CloseHandle(hPToken);
        //        //CloseHandle(hProcess);
        //        throw new Win32Exception(Marshal.GetLastWin32Error());
        //        //return false;
        //    }
        //    CloseHandle(hPToken); // no longer needed

        //    IntPtr hEnvBlock = GetEnvironmentBlock(hUserTokenDup);

        //    // By default CreateProcessAsUser creates a process on a non-interactive window station, meaning
        //    // the window station has a desktop that is invisible and the process is incapable of receiving
        //    // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user 
        //    // interaction with the new process.
        //    STARTUPINFO si = new STARTUPINFO();
        //    si.cb = (int)Marshal.SizeOf(si);
        //    si.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop
        //    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
        //    si.wShowWindow = SW_SHOW;

        //    // flags that specify the priority and creation method of the process
        //    //int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
        //    int dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;

        //    // create a new process in the current user's logon session
        //    bool result = CreateProcessAsUser(hUserTokenDup,        // client's access token
        //                                    null,                   // file to execute
        //                                    applicationName,        // command line
        //                                    ref sa,                 // pointer to process SECURITY_ATTRIBUTES
        //                                    ref sa,                 // pointer to thread SECURITY_ATTRIBUTES
        //                                    false,                  // handles are not inheritable
        //                                    dwCreationFlags,        // creation flags
        //                                    hEnvBlock,            // pointer to new environment block 
        //                                    null,                   // name of current directory 
        //                                    ref si,                 // pointer to STARTUPINFO structure
        //                                    out procInfo            // receives information about new process
        //                                    );

        //    if (hEnvBlock != IntPtr.Zero)
        //    {
        //        DestroyEnvironmentBlock(hEnvBlock);
        //    }

        //    // invalidate the handles
        //    CloseHandle(hUserTokenDup);
        //    //CloseHandle(hPToken);
        //    //CloseHandle(hProcess);

        //    if (!result)
        //    {
        //        throw new Win32Exception(Marshal.GetLastWin32Error());
        //    }

        //    // return true;
        //}

        private static IntPtr GetEnvironmentBlock(IntPtr token)
        {

            IntPtr envBlock = IntPtr.Zero;
            bool retVal = CreateEnvironmentBlock(ref envBlock, token, false);
            if (retVal == false)
            {
                //Environment Block, things like common paths to My Documents etc. 
                //Will not be created if "false" 
                //It should not adversley affect CreateProcessAsUser. 
                //string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error());
            }

            return envBlock;
        }


        internal static IDictionary<int, string> GetLoggedOnSessions()
        {
            return GetLoggedOnSessions(WTS_CURRENT_SERVER_HANDLE);
        }

        internal static IDictionary<int, string> GetLoggedOnSessions(String ServerName)
        {
            IDictionary<int, string> resultList;
            IntPtr serverHandle = WTSOpenServer(ServerName);

            try
            {
                resultList = GetLoggedOnSessions(serverHandle);
            }
            finally
            {
                WTSCloseServer(serverHandle);
            }

            return resultList;
        }

        private static IDictionary<int, string> GetLoggedOnSessions(IntPtr serverHandle)
        {
            Dictionary<int, string> resultList = null;

            IntPtr SessionInfoPtr = IntPtr.Zero;
            IntPtr bufferPtr;
            Int32 sessionCount = 0;
            Int32 retVal = WTSEnumerateSessions(serverHandle, 0, 1, ref SessionInfoPtr, ref sessionCount);
            Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
            Int32 currentSession = (int)SessionInfoPtr;
            uint bytes = 0;

            if (retVal != 0)
            {
                resultList = new Dictionary<int, string>();
                for (int i = 0; i < sessionCount; i++)
                {
                    WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)currentSession, typeof(WTS_SESSION_INFO));
                    currentSession += dataSize;

                    if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive
                        || si.State == WTS_CONNECTSTATE_CLASS.WTSConnected
                        || si.State == WTS_CONNECTSTATE_CLASS.WTSDisconnected
                        )
                    {
                        WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSUserName, out bufferPtr, out bytes);
                        string username = Marshal.PtrToStringAnsi(bufferPtr);
                        if (!String.IsNullOrEmpty(username)) // this is not a vlid user session
                        {
                            WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSDomainName, out bufferPtr, out bytes);
                            string domain = Marshal.PtrToStringAnsi(bufferPtr);

                            resultList.Add(si.SessionID, domain + @"\" + username);
                        }
                    }
                }

                WTSFreeMemory(SessionInfoPtr);
            }

            return resultList;
        }

        internal static string GetProcessExePath(Process process)
        {
            string processFilePath;
            try
            {
                processFilePath = System.IO.Path.GetFullPath(process.MainModule.FileName);
            }
            catch (Win32Exception)
            {
                using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT ExecutablePath, CommandLine from Win32_Process WHERE ProcessId = " + process.Id.ToString()))
                {
                    ManagementObject proc = searcher.Get().OfType<ManagementObject>().FirstOrDefault();
                    processFilePath = PWTProcess.GetProcessExePath(proc);
                    proc.Dispose();
                }
            }

            return processFilePath;
        }

        internal static FileVersionInfo GetCachedFileVersionInfo(string filename)
        {
            FileVersionInfo fileVersionInfo;

            if (_fileVersionInfoCache.ContainsKey(filename))
            {
                fileVersionInfo = (FileVersionInfo)_fileVersionInfoCache[filename];
            }
            else
            {
                fileVersionInfo = FileVersionInfo.GetVersionInfo(filename);
                _fileVersionInfoCache[filename] = fileVersionInfo;
            }

            return fileVersionInfo;
        }

        internal static string GetProcessDescription(Process process)
        {
            string description;
            try
            {
                if (process.MainModule != null)
                {
                    string name = System.IO.Path.GetFileName(process.MainModule.FileName);
                    description = name;
                    FileVersionInfo fileVersionInfo = GetCachedFileVersionInfo(process.MainModule.FileName.ToUpper());

                    if (fileVersionInfo != null)
                    {
                        string desc = fileVersionInfo.FileDescription;
                        if (!string.IsNullOrEmpty(desc) && System.IO.Path.GetFileName(desc) != name)
                        {
                            description = string.Format("{0} ({1})", desc, name);
                        }
                    }
                }
                else
                {
                    string name = process.ProcessName;
                    description = name;
                }
            }
            catch (Win32Exception)
            {
                using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Name, Description from Win32_Process WHERE ProcessId = " + process.Id.ToString()))
                {
                    ManagementObject proc = searcher.Get().OfType<ManagementObject>().FirstOrDefault();
                    description = PWTProcess.GetProcessDescription(proc);
                    proc.Dispose();
                }
            }

            return description;
        }
    }
}